﻿// ---------------------------------------------------------------------------------------------------------------------
//   Copyright (c) SRT Solutions 2009. All rights reserved.
// ---------------------------------------------------------------------------------------------------------------------

/* •—————————————————————————————————————————————————————————•
   | Adapted from MatthewPodwysocki's Functional C# Samples: |
   |                                                         |
   | http://code.msdn.microsoft.com/FunctionalCSharp         |
   •—————————————————————————————————————————————————————————• */

namespace SRTSolutions.Elevate.PatternMatching
{
    using global::System;
    using global::System.Collections.Generic;

    /// <summary>
    /// A pattern matching block with the return value and the
    /// the type to match on specified.
    /// </summary>
    /// <typeparam name="T">The type of the value being matched.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public class PatternMatch<T, TResult>
    {
        private readonly T value;
        private readonly List<Tuple<Func<T, bool>, Func<T, TResult>>> cases 
            = new List<Tuple<Func<T, bool>, Func<T, TResult>>>();
        private Func<T, TResult> elseFunc;

        internal PatternMatch(T value)
        {
            this.value = value;
        }

        /// <summary>
        /// Specifies a condition to match against, and the value to return
        /// </summary>
        /// <param name="matchCondition">The condition to match with.</param>
        /// <param name="matchResult">The result to return if the condition matches.</param>
        /// <returns>The current PatternMatch</returns>
        public PatternMatch<T, TResult> With(Func<T, bool> matchCondition, Func<T, TResult> matchResult)
        {
            if (matchCondition == null)
                throw new ArgumentNullException("matchCondition", "matchCondition is null.");
            if (matchResult == null)
                throw new ArgumentNullException("matchResult", "matchResult is null.");

            cases.Add(Tuple.Create(matchCondition, matchResult));
            return this;
        }

        /// <summary>
        /// Specifies a default result if all conditions fail to match.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <returns>The current PatternMatch</returns>
        public PatternMatch<T, TResult> Else(Func<T, TResult> result)
        {
            if (elseFunc != null)
                throw new InvalidOperationException("Cannot have multiple else cases");

            elseFunc = result;
            return this;
        }

        /// <summary>
        /// Evaluates this pattern match, returning the result of the condition that matches.
        /// </summary>
        /// <returns>the match resulg</returns>
        /// <remarks>If this does not contain a match, this method
        /// throws an IncompleteMatchException</remarks>
        public TResult EndMatch()
        {
            if (elseFunc != null)
                cases.Add(
                    Tuple.Create<Func<T, bool>, Func<T, TResult>>(x => true, elseFunc));
            foreach (var item in cases)
                if (item.Item1(value))
                    return item.Item2(value);

            throw new IncompleteMatchException("Incomplete pattern match");
        }
    }
}
